package service.impl;

import common.consts.*;
import dao.*;
import domain.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import service.ApplyService;
import service.ServiceResponse;
import service.util.ConditionMatcher;
import service.util.MessagePushUtil;

import java.util.Date;

/**
 * Created by aceyin on 15/6/29.
 */
@Service
public class ApplyServiceImpl extends ServiceBase implements ApplyService {

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private RecruitmentDao recruitmentDao;
    @Autowired
    private OfferDao offerDao;
    @Autowired
    private ApplyConditionDao applyConditionDao;
    @Autowired
    private UserProfileDao userProfileDao;
    @Autowired
    private UserDataProfileDao userDataProfileDao;
    @Autowired
    private PushMessageDao pushMessageDao;

    /**
     * 处理申请工作的逻辑，在数据合法的情况下：
     * <pre>
     * 1. 创建一个Offer
     * 2. 更新UserDataProfile
     * 3. 更新招聘本身的 apply_quantity
     * 4. 给商家发送Push消息
     * </pre>
     *
     * @param applier
     * @param recruitmentId
     * @return
     */
    @Override
    @Transactional(readOnly = false)
    public ServiceResponse apply(long applier, long recruitmentId) {
        Account user = accountDao.get(applier);
        ServiceResponse accountStatus = checkAccountStatus(user);

        if (accountStatus.isFailed()) {
            return accountStatus;
        }

        Recruitment recruitment = recruitmentDao.get(recruitmentId);

        ServiceResponse recruitmentStatus = checkRecruitmentStatus(recruitment);
        if (recruitmentStatus.isFailed()) {
            return recruitmentStatus;
        }

        // check if current user has already applied the recruitment
        Offer apply = offerDao.queryByApplierAndRecruitmentId(applier, recruitmentId);
        if (apply != null) {
            return ServiceResponse.fail(Consts.i18nKey.USER_HAS_APPLYED_THE_JOB_BEFORE);
        }

        // check if the applier matches the recruitment requirements
        ServiceResponse response = checkConditionMatches(user, recruitment);
        if (response.isFailed()) {
            return response;
        }

        boolean autoRecruit = YesNo.isYes(recruitment.getAutoRecruit());

        long owner = recruitment.getOwner();

        createOffer(applier, owner, recruitmentId, autoRecruit);

        updateUserDataProfile(applier, autoRecruit);

        // update apply_quantity
        recruitmentDao.increaseApplyQuantity(1, recruitmentId);
        // TODO : send notification to saler
        PushMessage message = MessagePushUtil.createWorkApplyMessage(user.getNick(), owner);
        pushMessageDao.insert(message);

        return ServiceResponse.success();
    }

    private void updateUserDataProfile(long applier, boolean autoRecruit) {
        UserDataProfile dataProfile = userDataProfileDao.get(applier);
        dataProfile.setWorkApplyTimes(dataProfile.getWorkApplyTimes() + 1);
        if (autoRecruit) {
            dataProfile.setEmployedTimes(dataProfile.getEmployedTimes() + 1);
        }

        userDataProfileDao.update(dataProfile);
    }

    private void createOffer(long applier, long owner, long recruitmentId, boolean autoRecruit) {
        String status = Offers.Status.WAITING.name();
        if (autoRecruit) {
            status = Offers.Status.EMPLOYED.name();
        }

        // insert a work_apply record
        Offer offer = new Offer();
        offer.setStatus(status);
        offer.setApplier(applier);
        offer.setApplyTime(new Date());
        offer.setEmployer(owner);
        offer.setRecruitmentId(recruitmentId);
        offer.setPayResult(Offers.PayResult.UN_PAYED.name());
        offer.setRateResult(Offers.RateResult.UN_RATE.name());
        offerDao.insert(offer);
    }

    private ServiceResponse checkConditionMatches(Account account, Recruitment recruitment) {
        ApplyCondition applyCondition = applyConditionDao.getByRecruitmentId(recruitment.getId());
        if (applyCondition == null) {
            return ServiceResponse.success();
        }
        UserProfile profile = userProfileDao.get(account.getId());
        if (profile == null) {
            return ServiceResponse.fail(Consts.i18nKey.APPLY_FAIL_DUE_TO_NO_USER_PROFILE_FOUND);
        }
        return ConditionMatcher.checkWithResponse(profile, applyCondition);
    }

    private ServiceResponse checkRecruitmentStatus(Recruitment recruitment) {
        if (recruitment == null) {
            return ServiceResponse.fail(Consts.i18nKey.UNKNOWN_RECRUITMENT);
        }
        String status = recruitment.getStatus();
        if (Recruitments.Status.isCanceled(status)) {
            return ServiceResponse.fail(Consts.i18nKey.RECRUITMENT_IS_CANCELED);
        }
        if (Recruitments.Status.isIncomplete(status)) {
            return ServiceResponse.fail(Consts.i18nKey.RECRUITMENT_IS_INCOMPELETE);
        }
        if (Recruitments.Status.isEnded(status)) {
            return ServiceResponse.fail(Consts.i18nKey.RECRUITMENT_IS_ENDED);
        }
        Date endTime = recruitment.getApplyEndTime();
        Date now = new Date();
        if (now.after(endTime)) {
            return ServiceResponse.fail(Consts.i18nKey.RECRUITMENT_APPLY_TIME_IS_ENDED);
        }
        return ServiceResponse.success();
    }

    private ServiceResponse checkAccountStatus(Account account) {
        if (account == null) {
            return ServiceResponse.fail(Consts.i18nKey.UNKNOWN_USER);
        }

        String status = account.getStatus();
        if (Accounts.Status.isLocked(status)) {
            return ServiceResponse.fail(Consts.i18nKey.ACCOUNT_IS_LOCKED);
        }

        if (Accounts.Status.isAbnormal(status)) {
            return ServiceResponse.fail(Consts.i18nKey.ACCOUNT_IS_ABNORMAL);
        }
        return ServiceResponse.success();
    }

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void setRecruitmentDao(RecruitmentDao recruitmentDao) {
        this.recruitmentDao = recruitmentDao;
    }

    public void setOfferDao(OfferDao offerDao) {
        this.offerDao = offerDao;
    }

    public void setApplyConditionDao(ApplyConditionDao applyConditionDao) {
        this.applyConditionDao = applyConditionDao;
    }

    public void setUserDataProfileDao(UserDataProfileDao userDataProfileDao) {
        this.userDataProfileDao = userDataProfileDao;
    }

    public void setPushMessageDao(PushMessageDao pushMessageDao) {
        this.pushMessageDao = pushMessageDao;
    }

    public void setUserProfileDao(UserProfileDao userProfileDao) {
        this.userProfileDao = userProfileDao;
    }
}
